home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
FM Towns: Free Software Collection 7
/
FM Towns Free Software Collection 7.iso
/
taropyon
/
zmodem
/
src
/
rz.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-11-30
|
35KB
|
1,631 lines
#define __TOWNS__
#define VERSION "3.04 02-20-91"
#define PUBDIR "/usr/spool/uucppublic"
/*
* rz.c By Chuck Forsberg
*
* A program for Unix to receive files and commands from computers running
* Professional-YAM, PowerCom, YAM, IMP, or programs supporting XMODEM. rz
* uses Unix buffered input to reduce wasted CPU time.
*
*
* This version implements numerous enhancements including ZMODEM Run Length
* Encoding and variable length headers. These features were not funded by
* the original Telenet development contract.
*
* This software may be freely used for non commercial and educational (didactic
* only) purposes. This software may also be freely used to support file
* transfer operations to or from licensed Omen Technology products. Any
* programs which use part or all of this software must be provided in source
* form with this notice intact except by written permission from Omen
* Technology Incorporated.
*
* Use of this software for commercial or administrative purposes except when
* exclusively limited to interfacing Omen Technology products requires a per
* port license payment of $20.00 US per port (less in quantity). Use of
* this code by inclusion, decompilation, reverse engineering or any other
* means constitutes agreement to these conditions and acceptance of
* liability to license the materials and payment of reasonable legal costs
* necessary to enforce this license agreement.
*
*
* Omen Technology Inc FAX: 503-621-3745 Post Office Box 4681
* Portland OR 97208
*
* This code is made available in the hope it will be useful, BUT WITHOUT ANY
* WARRANTY OF ANY KIND OR LIABILITY FOR ANY DAMAGES OF ANY KIND.
*
*
*
* Iff the program is invoked by rzCOMMAND, output is piped to "COMMAND
* filename" (Unix only)
*
* Some systems (Venix, Coherent, Regulus) may not support tty raw mode read(2)
* the same way as Unix. ONEREAD must be defined to force one character reads
* for these systems. Added 7-01-84 CAF
*
* Alarm signal handling changed to work with 4.2 BSD 7-15-84 CAF
*
* NFGVMIN Updated 2-18-87 CAF for Xenix systems where c_cc[VMIN] doesn't work
* properly (even though it compiles without error!),
*
* SEGMENTS=n added 2-21-88 as a model for CP/M programs for CP/M-80 systems
* that cannot overlap modem and disk I/O.
*
* -DMD may be added to compiler command line to compile in Directory-creating
* routines from Public Domain TAR by John Gilmore
*
* HOWMANY may be tuned for best performance
*
* USG UNIX (3.0) ioctl conventions courtesy Jeff Martin */
#define LOGFILE "/tmp/rzlog"
#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
#include <ctype.h>
#include <errno.h>
extern int errno;
#ifdef __TOWNS__
# include <string.h>
# include <stdarg.h>
# include <stdlib.h>
# include <direct.h>
# include <stat.h>
# include <sys/types.h>
# include <sys/utime.h>
# include <io.h>
# include <time.h>
# include <splib.h>
# include <fslib.h>
# include "usrlib.h"
# include "rsctrl.h"
# include "flib.h"
# include "msgdat.h"
# ifdef __HIGHC__
# pragma On(Align_labels);
# endif
extern int RsPort;
# define _FSTAT_IGN
# define MD
#endif
#include "prot.h"
#include "rz.h"
int Zmodem = 0; /* ZMODEM protocol requested */
int Nozmodem = 0; /* If invoked as "rb" */
unsigned Baudrate = 2400;
unsigned Effbaud = 2400;
#include "rbsb.c" /* most of the system dependent stuff here */
#include "crctab.c"
FILE *fout = NULL;
/*
* Routine to calculate the free bytes on the current file system ~0 means
* many free bytes (unknown)
*/
static long getfree(void)
{
#ifdef __TOWNS__
int drv;
int tf, wf;
if ( (drv = FS_getdrv()) >= 0 )
{
if ( FS_get_dskFree( drv, &tf, &wf) == NORMAL )
return (long)tf;
}
#endif
return (~0L); /* many free bytes ... */
}
int Lastrx;
long rxbytes;
int Crcflg;
int Firstsec;
int Eofseen; /* indicates cpm eof (^Z) has been received */
int errors;
int Restricted = 0; /* restricted; no /.. or ../ in filenames */
#ifdef ONEREAD
/* Sorry, Regulus and some others don't work right in raw mode! */
int Readnum = 1; /* Number of bytes to ask for in read() from
* modem */
#else
int Readnum = HOWMANY; /* Number of bytes to ask for in read() from
* modem */
#endif
#define DEFBYTL 2000000000L /* default rx file size */
long Bytesleft; /* number of bytes of incoming file left */
long Modtime; /* Unix style mod time for incoming file */
int Filemode; /* Unix style mode for incoming file */
long Totalleft;
long Filesleft;
char Pathname[PATHLEN];
char *Progname; /* the name by which we were called */
int Batch = 0;
int Topipe = 0;
int Verbose = 0;
int Thisbinary; /* current file is to be received in bin mode */
int Blklen; /* record length of received packets */
#ifdef SEGMENTS
int chinseg = 0; /* Number of characters received in this data seg */
char secbuf[1 + (SEGMENTS + 1) * 1024];
#else
char secbuf[1025];
#endif
char linbuf[HOWMANY];
int Lleft = 0; /* number of characters in linbuf */
time_t timep[2];
char zconv; /* ZMODEM file conversion request */
char zmanag; /* ZMODEM file management request */
char ztrans; /* ZMODEM file transport request */
int Zctlesc; /* Encode control characters */
int Zrwindow = 1400; /* RX window size (controls garbage count) */
jmp_buf tohere; /* For the interrupt on RX timeout */
#define xsendline(c) sendline(c)
#include "zm.c"
#include "zmr.c"
int tryzhdrtype = ZRINIT; /* Header type to send corresponding
* to Last rx close */
#ifdef __TOWNS__
void signal_func(int level)
{
}
/* called by signal interrupt or terminate to clean things up */
void bibi(int n)
{
if (Zmodem)
zmputs(Attn);
canit();
mode(0);
USR_fprintf(stderr, "rz: caught signal %d; exiting", n);
cucheck();
EXIT(128 + n);
}
#else
void alrm(int level)
{
longjmp(tohere, -1);
}
/* called by signal interrupt or terminate to clean things up */
bibi(int n)
{
if (Zmodem)
zmputs(Attn);
canit();
mode(0);
fprintf(stderr, "rz: caught signal %d; exiting", n);
cucheck();
exit(128 + n);
}
#endif
void zm_main(int argc, char *argv[])
{
register char *cp;
register npats;
char *virgin, **patts;
int exitcode = 0;
Rxtimeout = 100;
// setbuf(stderr, NULL);
#if 0
if ((cp = getenv("SHELL")) && (substr(cp, "rsh") || substr(cp, "rksh")))
Restricted = TRUE; /* 制限 */
#endif
from_cu();
chkinvok(virgin = argv[0]); /* if called as [-]rzCOMMAND set flag */
npats = 0;
while (--argc)
{
cp = *++argv;
if (*cp == '-')
{
while (*++cp)
{
switch (*cp)
{
case '\\':
cp[1] = toupper(cp[1]);
continue;
case 'c':
Crcflg = TRUE;
break;
case 'e':
Zctlesc = 1;
break;
case 't':
if (--argc < 1)
{
usage();
}
Rxtimeout = atoi(*++argv);
if (Rxtimeout < 10 || Rxtimeout > 1000)
usage();
break;
case 'w':
if (--argc < 1)
{
usage();
}
Zrwindow = atoi(*++argv);
break;
case 'v':
++Verbose;
break;
default:
usage();
}
}
} else if (!npats && argc > 0)
{
if (argv[0][0])
{
npats = argc;
patts = argv;
}
}
}
if (npats > 1)
usage();
if (Batch && npats)
usage();
#ifndef __TOWNS__
if (Verbose)
{
if ( freopen(LOGFILE, "a", stderr) == NULL )
{
printf("Can't open log file %s\n", LOGFILE);
exit(0200);
}
// setbuf(stderr, NULL);
USR_fprintf(stderr, "argv[0]=%s Progname=%s\n", virgin, Progname);
}
#endif
if (Fromcu)
{
if (Verbose == 0)
Verbose = 2;
}
#ifdef __TOWNS__
USR_fprintf(stderr,"\n");
USR_fprintf(stderr,"rz ver.%s for TownsOS", MAIN_VER );
USR_fprintf(stderr," orignal rz %s\n\n", VERSION);
#else
vfile("%s %s for %s\n", Progname, VERSION, OS);
#endif
mode(1); /* save old tty stat, set raw mode */
#ifndef __TOWNS__
if (signal(SIGINT, bibi) == SIG_IGN)
{
signal(SIGINT, SIG_IGN);
signal(SIGKILL, SIG_IGN);
} else
{
signal(SIGINT, bibi);
signal(SIGKILL, bibi);
}
signal(SIGTERM, bibi);
#endif
if ( wcreceive(npats, patts) == ERROR )
{
exitcode = 0200;
canit();
}
mode(0);
if (exitcode && !Zmodem) /* bellow again with all thy might. */
canit();
if (exitcode)
cucheck();
EXIT(exitcode ? exitcode : 0);
}
#include "rzhelp.c"
/*
* Debugging information output interface routine
*/
/* VARARGS1 */
static void vfile( CONST char *form, ... )
{
va_list arg;
char tmp[BUFSIZ];
if ( Verbose > 2 )
{
va_start( arg, form );
vsprintf( tmp, form, arg );
va_end(arg);
USR_fputs(tmp, stderr );
USR_fputs("\n",stderr);
}
}
/*
* Let's receive something already.
*/
#ifndef __TOWNS__
static char *rbmsg =
"%s ready. To begin transfer, type \"%s file ...\" to your modem program\r\n\n";
#endif
static int wcreceive(int argc, char **argp)
{
register c;
if (Batch || argc == 0)
{
Crcflg = 1;
#ifdef __TOWNS__
USR_fprintf(stderr,
"rx ready. To begin transfer, type \"%s file ...\" to your modem program\r\n",
Nozmodem ? "sb" : "sz" );
#else
USR_fprintf(stderr, rbmsg, Progname, Nozmodem ? "sb" : "sz");
#endif
if ( (c = tryz()) != 0 )
{
if (c == ZCOMPL)
return OK;
if (c == ERROR)
goto fubar;
c = rzfiles();
if (c)
goto fubar;
} else
{
for (;;)
{
if (wcrxpn(secbuf) == ERROR)
goto fubar;
if (secbuf[0] == 0)
return OK;
if (procheader(secbuf) == ERROR)
goto fubar;
if (wcrx() == ERROR)
goto fubar;
}
}
} else
{
Bytesleft = DEFBYTL;
Filemode = 0;
Modtime = 0L;
procheader("");
strcpy(Pathname, *argp);
checkpath(Pathname);
USR_fprintf(stderr, "\nrz: ready to receive %s\r\n", Pathname);
if ((fout = FM_fopen(Pathname, "wb")) == NULL)
return ERROR;
if (wcrx() == ERROR)
goto fubar;
}
return OK;
fubar:
canit();
#ifndef __TOWNS__
if (Topipe && fout)
{
pclose(fout);
return ERROR;
}
#endif
Modtime = 1;
if (fout)
FM_fclose(fout);
if (Restricted)
{
unlink(Pathname);
USR_fprintf(stderr, "\r\nrz: %s removed.\r\n", Pathname);
}
return ERROR;
}
/*
* Fetch a pathname from the other end as a C ctyle ASCIZ string. Length is
* indeterminate as long as less than Blklen A null string represents no more
* files (YMODEM)
*/
static int wcrxpn(char *rpn)
/* receive a pathname */
{
register c;
#ifdef NFGVMIN
readline(1);
#else
purgeline();
#endif
et_tu:
Firstsec = TRUE;
Eofseen = FALSE;
sendline(Crcflg ? WANTCRC : NAK);
Lleft = 0; /* Do read next time ... */
while ((c = wcgetsec(rpn, 100)) != 0)
{
if (c == WCEOT)
{
zperr("Pathname fetch returned %d", c);
sendline(ACK);
Lleft = 0; /* Do read next time ... */
readline(1);
goto et_tu;
}
return ERROR;
}
sendline(ACK);
return OK;
}
/*
* Adapted from CMODEM13.C, written by Jack M. Wierda and Roderick W. Hart
*/
static int wcrx(void)
{
register int sectnum, sectcurr;
register char sendchar;
register char *p;
int cblklen; /* bytes to dump this block */
Firstsec = TRUE;
sectnum = 0;
Eofseen = FALSE;
sendchar = Crcflg ? WANTCRC : NAK;
for (;;)
{
sendline(sendchar); /* send it now, we're ready! */
Lleft = 0; /* Do read next time ... */
sectcurr = wcgetsec(secbuf, (sectnum & 0177) ? 50 : 130);
report(sectcurr);
if (sectcurr == (sectnum + 1 & 0377))
{
sectnum++;
cblklen = Bytesleft > Blklen ? Blklen : Bytesleft;
if (putsec(secbuf, cblklen) == ERROR)
return ERROR;
if ((Bytesleft -= cblklen) < 0)
Bytesleft = 0;
sendchar = ACK;
} else if (sectcurr == (sectnum & 0377))
{
zperr("Received dup Sector");
sendchar = ACK;
} else if (sectcurr == WCEOT)
{
if (closeit())
return ERROR;
sendline(ACK);
Lleft = 0; /* Do read next time ... */
return OK;
} else if (sectcurr == ERROR)
return ERROR;
else
{
zperr("Sync Error");
return ERROR;
}
}
}
/*
* Wcgetsec fetches a Ward Christensen type sector. Returns sector number
* encountered or ERROR if valid sector not received, or CAN CAN received or
* WCEOT if eot sector time is timeout for first char, set to 4 seconds
* thereafter **************** NO ACK IS SENT IF SECTOR IS RECEIVED OK **************
* (Caller must do that when he is good and ready to get next sector)
*/
static int wcgetsec(char *rxbuf, int maxtime)
{
register checksum, wcj, firstch;
register unsigned short oldcrc;
register char *p;
int sectcurr;
for (Lastrx = errors = 0; errors < RETRYMAX; errors++)
{
if ((firstch = readline(maxtime)) == STX)
{
Blklen = 1024;
goto get2;
}
if (firstch == SOH)
{
Blklen = 128;
get2:
sectcurr = readline(1);
if ((sectcurr + (oldcrc = readline(1))) == 0377)
{
oldcrc = checksum = 0;
for (p = rxbuf, wcj = Blklen; --wcj >= 0;)
{
if ((firstch = readline(1)) < 0)
goto bilge;
oldcrc = updcrc(firstch, oldcrc);
checksum += (*p++ = firstch);
}
if ((firstch = readline(1)) < 0)
goto bilge;
if (Crcflg)
{
oldcrc = updcrc(firstch, oldcrc);
if ((firstch = readline(1)) < 0)
goto bilge;
oldcrc = updcrc(firstch, oldcrc);
if (oldcrc & 0xFFFF)
zperr("CRC");
else
{
Firstsec = FALSE;
return sectcurr;
}
} else if (((checksum - firstch) & 0377) == 0)
{
Firstsec = FALSE;
return sectcurr;
} else
zperr("Checksum");
} else
zperr("Sector number garbled");
}
/* make sure eot really is eot and not just mixmash */
#ifdef NFGVMIN
else if (firstch == EOT && readline(1) == TIMEOUT)
return WCEOT;
#else
else if (firstch == EOT && Lleft == 0)
return WCEOT;
#endif
else if (firstch == CAN)
{
if (Lastrx == CAN)
{
zperr("Sender CANcelled");
return ERROR;
} else
{
Lastrx = CAN;
continue;
}
} else if (firstch == TIMEOUT)
{
if (Firstsec)
goto humbug;
bilge:
zperr("TIMEOUT");
} else
zperr("Got 0%o sector header", firstch);
humbug:
Lastrx = 0;
while (readline(1) != TIMEOUT)
;
if (Firstsec)
{
sendline(Crcflg ? WANTCRC : NAK);
Lleft = 0; /* Do read next time ... */
} else
{
maxtime = 40;
sendline(NAK);
Lleft = 0; /* Do read next time ... */
}
}
/* try to stop the bubble machine. */
canit();
return ERROR;
}
/*
* This version of readline is reasoably well suited for reading many
* characters. (except, currently, for the Regulus version!)
*
* timeout is in tenths of seconds
*/
static int readline(int timeout)
{
register n;
static char *cdq; /* pointer for removing chars from linbuf */
if ( --Lleft >= 0 )
{
if (Verbose > 8)
{
USR_fprintf(stderr, "%02x ", *cdq & 0377);
}
return (*cdq++ & 0377);
}
#ifdef __TOWNS__
n = timeout;
if (n < 200 )
n = 200;
#else
n = timeout / 10;
if (n < 2)
n = 3;
#endif
if (Verbose > 5)
USR_fprintf(stderr, "Calling read: alarm=%d Readnum=%d ",
n, Readnum);
#ifdef __TOWNS__
{
clock_t clk;
clk = H_CLOCK2(0) + n * CLOCKS_PER_SEC / 100;
int c;
do
{
if ( (c = RS_chk(RsPort)) > 0 )
{
int i;
i = 0;
while ( i < Readnum )
{
linbuf[i++] = RS_getc(RsPort);
if ( RS_chk(RsPort) < 1 )
break;
}
cdq = linbuf;
Lleft = i;
break;
}
} while ( clk > H_CLOCK2(clk) );
if ( c < 1 )
{ /* timeout */
Lleft = 0;
if (Verbose > 1)
USR_fprintf(stderr, "Readline:TIMEOUT\n");
return TIMEOUT;
}
}
#else
if (setjmp(tohere))
{
#ifdef TIOCFLUSH
/* ioctl(0, TIOCFLUSH, 0); */
#endif
Lleft = 0;
if (Verbose > 1)
fprintf(stderr, "Readline:TIMEOUT\n");
return TIMEOUT;
}
signal(SIGALRM, alrm);
alarm(n);
Lleft = read(0, cdq = linbuf, Readnum);
alarm(0);
#endif
if (Verbose > 5)
{
USR_fprintf(stderr, "Read returned %d bytes\n", Lleft);
}
if (Lleft < 1)
return TIMEOUT;
--Lleft;
if (Verbose > 8)
{
USR_fprintf(stderr, "%02X ", *cdq & 0377);
}
return (*cdq++ & 0377);
}
/*
* Purge the modem input queue of all characters
*/
static void purgeline(void)
{
Lleft = 0;
#ifndef __TOWNS__
# ifdef USG
ioctl(0, TCFLSH, 0);
# else
lseek(0, 0L, 2);
# endif
#endif
}
/*
* Process incoming file information header
*/
static int procheader(char *name)
{
register char *openmode, *p, **pp;
static dummy;
struct stat f;
/* set default parameters and overrides */
openmode = "wb";
/*
* Process ZMODEM remote file management requests
*/
Thisbinary = (zconv != ZCNL); /* Remote ASCII override */
if (zmanag == ZMAPND)
openmode = "ab";
Bytesleft = DEFBYTL;
Filemode = 0;
Modtime = 0L;
p = name + 1 + strlen(name);
if (*p)
{ /* file coming from Unix or DOS system */
sscanf(p, "%ld%lo%o%lo%d%ld%d%d",
&Bytesleft, &Modtime, &Filemode,
&dummy, &Filesleft, &Totalleft, &dummy, &dummy);
if (Filemode & UNIXFILE)
++Thisbinary;
if (Verbose)
{
USR_fprintf(stderr, "\nIncoming: %s %ld %lo %o\n",
name, Bytesleft, Modtime, Filemode);
USR_fprintf(stderr, "YMODEM header: %s\n", p);
}
} else
{
#ifdef __TOWNS__
#else
/* File coming from CP/M system */
for (p = name; *p; ++p) /* change / to _ */
if (*p == '/')
*p = '_';
if (*--p == '.') /* zap trailing period */
*p = 0;
#endif
}
strcpy(Pathname, name);
checkpath(name);
if (*name && stat(name, &f) != -1)
{
zmanag &= ZMMASK;
vfile("Current %s is %ld %lo", name, f.st_size, f.st_mtime);
if (Thisbinary && zconv == ZCRESUM)
{
rxbytes = f.st_size & ~511;
openit(name, "r+b");
if (!fout)
return ZFERR;
if (Bytesleft < rxbytes)
rxbytes = 0;
if (fseek(fout, rxbytes, 0))
{
closeit();
return ZFERR;
}
vfile("Crash recovery at %ld", rxbytes);
return 0;
} else if ((zmanag == ZMNEW) ||
((zmanag == ZMNEWL) && Bytesleft <= f.st_size))
{
if ((f.st_mtime + 1) >= Modtime)
goto skipfile;
goto doopen;
} else if (zmanag == ZMPROT)
goto skipfile;
else if ((zmanag & ZMMASK) != ZMCLOB)
goto skipfile;
goto doopen;
} else if (zmanag & ZMSKNOLOC)
{
skipfile:
vfile("Skipping %s", name);
return ZSKIP;
}
doopen:
openit(name, openmode);
#ifdef MD
if (!fout)
if (make_dirs(name))
openit(name, openmode);
#endif
if (!fout)
return ZFERR;
return 0;
}
static void openit(char *name, char *openmode)
{
#ifdef DEBUG
USR_fprintf(stderr,"\nfopen(\x22%s\x22,\x22%s\x22)\n", name, openmode);
#endif
fout = FM_fopen(name, openmode);
}
#ifdef MD
/*
* Directory-creating routines from Public Domain TAR by John Gilmore
*/
/*
* After a file/link/symlink/dir creation has failed, see if it's because
* some required directory was not present, and if so, create all required
* dirs.
*/
static int make_dirs(register char *pathname)
{
register char *p; /* Points into path */
int madeone = 0; /* Did we do anything yet? */
int save_errno = errno; /* Remember caller's errno */
if (errno != ENOENT)
return 0; /* Not our problem */
for (p = strchr(pathname, '/'); p != NULL; p = strchr(p + 1, '/'))
{
/* Avoid mkdir of empty string, if leading or double '/' */
if (p == pathname || p[-1] == '/')
continue;
/* Avoid mkdir where last part of path is '.' */
if (p[-1] == '.' && (p == pathname + 1 || p[-2] == '/'))
continue;
*p = 0; /* Truncate the path there */
#ifdef __TOWNS__
if (!_mkdir(pathname))
#else
if (!mkdir(pathname, 0777))
#endif
{ /* Try to create it as a dir */
vfile("Made directory %s\n", pathname);
madeone++; /* Remember if we made one */
*p = '/';
continue;
}
*p = '/';
if (errno == EEXIST) /* Directory already exists */
continue;
/*
* Some other error in the mkdir. We return to the caller.
*/
break;
}
errno = save_errno; /* Restore caller's errno */
return madeone; /* Tell them to retry if we made one */
}
#ifndef __TOWNS__
#if (MD != 2)
#define TERM_SIGNAL(status) ((status) & 0x7F)
#define TERM_COREDUMP(status) (((status) & 0x80) != 0)
#define TERM_VALUE(status) ((status) >> 8)
/*
* Make a directory. Compatible with the mkdir() system call on 4.2BSD.
*/
static int mkdir(char *dpath, int dmode)
{
int cpid, status;
struct stat statbuf;
if (stat(dpath, &statbuf) == 0)
{
errno = EEXIST; /* Stat worked, so it already exists */
return -1;
}
/* If stat fails for a reason other than non-existence, return error */
if (errno != ENOENT)
return -1;
switch (cpid = fork())
{
case -1: /* Error in fork() */
return (-1); /* Errno is set already */
case 0: /* Child process */
/*
* Cheap hack to set mode of new directory. Since this child
* process is going away anyway, we zap its umask. FIXME, this
* won't suffice to set SUID, SGID, etc. on this directory. Does
* anybody care?
*/
status = umask(0); /* Get current umask */
status = umask(status | (0777 & ~dmode)); /* Set for mkdir */
execl("/bin/mkdir", "mkdir", dpath, (char *) 0);
_exit(-1); /* Can't exec /bin/mkdir */
default: /* Parent process */
while (cpid != wait(&status)); /* Wait for kid to finish */
}
if (TERM_SIGNAL(status) != 0 || TERM_VALUE(status) != 0)
{
errno = EIO; /* We don't know why, but */
return -1; /* /bin/mkdir failed */
}
return 0;
}
#endif /* MD != 2 */
#endif /* endof ifndef __TOWNS__ */
#endif /* MD */
/*
* Putsec writes the n characters of buf to receive file fout. If not in
* binary mode, carriage returns, and all characters starting with CPMEOF are
* discarded.
*/
static int putsec(char *buf, register n)
{
register char *p;
if (n == 0)
return OK;
if (Thisbinary)
{
for (p = buf; --n >= 0;)
{
putc(*p, fout);
++p;
}
} else
{
if (Eofseen)
return OK;
for (p = buf; --n >= 0; ++p)
{
if (*p == '\r')
continue;
if (*p == CPMEOF)
{
Eofseen = TRUE;
return OK;
}
putc(*p, fout);
}
}
return OK;
}
/*
* Send a character to modem. Small is beautiful.
*/
static void sendline(int c)
{
c &= 0xFF;
if (Verbose > 6)
USR_fprintf(stderr, "Sendline: %x\n", c);
#ifdef __TOWNS__
RS_putc( RsPort, c );
#else
char d;
d = c;
write(1, &d, 1);
#endif
}
static void flushmo(void)
{
}
static void flushmoc(void)
{
}
/*
* substr(string, token) searches for token in string s returns pointer to
* token within string if found, NULL otherwise
*/
static char *substr(register char *s, register char *t)
{
register char *ss, *tt;
/* search for first char of token */
for (ss = s; *s; s++)
if (*s == *t)
/* compare token with substring */
for (ss = s, tt = t;;)
{
if (*tt == 0)
return s;
if (*ss++ != *tt++)
break;
}
return NULL;
}
/*
* Log an error
*/
/* VARARGS1 */
static void zperr(char *form, ... )
{
va_list arg;
char tmp[BUFSIZ];
if (Verbose <= 0)
return;
USR_fprintf(stderr, "Retry %d: ", errors);
va_start( arg, form );
vsprintf( tmp, form, arg );
va_end(arg);
USR_fputs(tmp, stderr );
USR_fputs("\n",stderr);
}
/* send cancel string to get the other end to shut up */
static void canit(void)
{
static char canistr[] =
{
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0
};
RS_puts(RsPort,canistr);
Lleft = 0; /* Do read next time ... */
}
static void report(int sct)
{
if (Verbose > 1)
USR_fprintf(stderr, "%03d%c", sct, sct % 10 ? ' ' : '\r');
}
/*
* If called as [-][dir/../]vrzCOMMAND set Verbose to 1 If called as
* [-][dir/../]rzCOMMAND set the pipe flag If called as rb use YMODEM
* protocol
*/
static void chkinvok(char *s)
{
register char *p;
p = s;
while (*p == '-')
s = ++p;
while (*p)
if (*p++ == '/')
s = p;
if (*s == 'v')
{
Verbose = 1;
++s;
}
Progname = s;
if (s[0] == 'r' && s[1] == 'z')
Batch = TRUE;
if (s[0] == 'r' && s[1] == 'b')
Batch = Nozmodem = TRUE;
#ifndef __TOWNS__
if (s[2] && s[0] == 'r' && s[1] == 'b')
Topipe = 1;
if (s[2] && s[0] == 'r' && s[1] == 'z')
Topipe = 1;
#endif
}
/*
* Totalitarian Communist pathname processing
*/
static void checkpath(char *name)
{
if (Restricted)
{
FILE *fp;
if ( (fp = FM_fopen(name, "rb")) != NULL)
{
canit();
USR_fprintf(stderr, "\r\nrz: %s exists\n", name);
FM_fclose(fp);
bibi(-1);
}
/* restrict pathnames to current tree or uucppublic */
if (substr(name, "../")
|| (name[0] == '/' && strncmp(name, PUBDIR, strlen(PUBDIR))))
{
canit();
USR_fprintf(stderr, "\r\nrz:\tSecurity Violation\r\n");
bibi(-1);
}
}
}
/*
* Initialize for Zmodem receive attempt, try to activate Zmodem sender
* Handles ZSINIT frame Return ZFILE if Zmodem filename received, -1 on
* error, ZCOMPL if transaction finished, else 0
*/
static int tryz(void)
{
register c, n;
register cmdzack1flg;
if (Nozmodem) /* Check for "rb" program name */
return 0;
for (n = Zmodem ? 15 : 5; --n >= 0;)
{
/* Set buffer length (0) and capability flags */
#ifdef SEGMENTS
stohdr(SEGMENTS * 1024L);
#else
stohdr(0L);
#endif
#ifdef CANBREAK
Txhdr[ZF0] = CANFC32 | CANFDX | CANOVIO | CANBRK;
#else
Txhdr[ZF0] = CANFC32 | CANFDX | CANOVIO;
#endif
if (Zctlesc)
Txhdr[ZF0] |= TESCCTL;
Txhdr[ZF0] |= CANRLE;
Txhdr[ZF1] = CANVHDR;
/* tryzhdrtype may == ZRINIT */
zshhdr(4, tryzhdrtype, Txhdr);
if (tryzhdrtype == ZSKIP) /* Don't skip too far */
tryzhdrtype = ZRINIT; /* CAF 8-21-87 */
again:
switch (zgethdr(Rxhdr, 0))
{
case ZRQINIT:
if (Rxhdr[ZF3] & 0x80)
Usevhdrs = 1; /* we can var header */
continue;
case ZEOF:
continue;
case TIMEOUT:
continue;
case ZFILE:
zconv = Rxhdr[ZF0];
zmanag = Rxhdr[ZF1];
ztrans = Rxhdr[ZF2];
if (Rxhdr[ZF3] & ZCANVHDR)
Usevhdrs = TRUE;
tryzhdrtype = ZRINIT;
c = zrdata(secbuf, 1024);
mode(3);
if (c == GOTCRCW)
return ZFILE;
zshhdr(4, ZNAK, Txhdr);
goto again;
case ZSINIT:
Zctlesc = TESCCTL & Rxhdr[ZF0];
if (zrdata(Attn, ZATTNLEN) == GOTCRCW)
{
stohdr(1L);
zshhdr(4, ZACK, Txhdr);
goto again;
}
zshhdr(4, ZNAK, Txhdr);
goto again;
case ZFREECNT:
stohdr(getfree());
zshhdr(4, ZACK, Txhdr);
goto again;
case ZCOMMAND:
cmdzack1flg = Rxhdr[ZF0];
if (zrdata(secbuf, 1024) == GOTCRCW)
{
if (cmdzack1flg & ZCACK1)
stohdr(0L);
else
stohdr((long) sys2(secbuf));
purgeline();/* dump impatient questions */
do
{
zshhdr(4, ZCOMPL, Txhdr);
}
while (++errors < 20 && zgethdr(Rxhdr, 1) != ZFIN);
ackbibi();
if (cmdzack1flg & ZCACK1)
exec2(secbuf);
return ZCOMPL;
}
zshhdr(4, ZNAK, Txhdr);
goto again;
case ZCOMPL:
goto again;
default:
continue;
case ZFIN:
ackbibi();
return ZCOMPL;
case ZCAN:
return ERROR;
}
}
return 0;
}
/*
* Receive 1 or more files with ZMODEM protocol
*/
static int rzfiles(void)
{
register c;
for (;;)
{
switch (c = rzfile())
{
case ZEOF:
case ZSKIP:
switch (tryz())
{
case ZCOMPL:
return OK;
default:
return ERROR;
case ZFILE:
break;
}
continue;
default:
return c;
case ERROR:
return ERROR;
}
}
}
/*
* Receive a file with ZMODEM protocol Assumes file name frame is in secbuf
*/
static int rzfile(void)
{
register c, n;
long rxbytes;
Eofseen = FALSE;
if (procheader(secbuf) == ERROR)
{
return (tryzhdrtype = ZSKIP);
}
n = 20;
rxbytes = 0l;
for (;;)
{
#ifdef SEGMENTS
chinseg = 0;
#endif
stohdr(rxbytes);
zshhdr(4, ZRPOS, Txhdr);
nxthdr:
switch (c = zgethdr(Rxhdr, 0))
{
default:
vfile("rzfile: zgethdr returned %d", c);
return ERROR;
case ZNAK:
case TIMEOUT:
#ifdef SEGMENTS
putsec(secbuf, chinseg);
chinseg = 0;
#endif
if (--n < 0)
{
vfile("rzfile: zgethdr returned %d", c);
return ERROR;
}
case ZFILE:
zrdata(secbuf, 1024);
continue;
case ZEOF:
#ifdef SEGMENTS
putsec(secbuf, chinseg);
chinseg = 0;
#endif
if (rclhdr(Rxhdr) != rxbytes)
{
/*
* Ignore eof if it's at wrong place - force a timeout
* because the eof might have gone out before we sent our
* zrpos.
*/
errors = 0;
goto nxthdr;
}
if (closeit())
{
tryzhdrtype = ZFERR;
vfile("rzfile: closeit returned <> 0");
return ERROR;
}
vfile("rzfile: normal EOF");
return c;
case ERROR: /* Too much garbage in header search error */
#ifdef SEGMENTS
putsec(secbuf, chinseg);
chinseg = 0;
#endif
if (--n < 0)
{
vfile("rzfile: zgethdr returned %d", c );
return ERROR;
}
zmputs(Attn);
continue;
case ZSKIP:
#ifdef SEGMENTS
putsec(secbuf, chinseg);
chinseg = 0;
#endif
Modtime = 1;
closeit();
vfile("rzfile: Sender SKIPPED file");
return c;
case ZDATA:
if (rclhdr(Rxhdr) != rxbytes)
{
if (--n < 0)
{
return ERROR;
}
#ifdef SEGMENTS
putsec(secbuf, chinseg);
chinseg = 0;
#endif
zmputs(Attn);
continue;
}
moredata:
if (Verbose > 1)
USR_fprintf(stderr, "\r%7ld ZMODEM%s ",
rxbytes, Crc32r ? " CRC-32" : "");
#ifdef SEGMENTS
if (chinseg >= (1024 * SEGMENTS))
{
putsec(secbuf, chinseg);
chinseg = 0;
}
switch (c = zrdata(secbuf + chinseg, 1024))
#else
switch (c = zrdata(secbuf, 1024))
#endif
{
case ZCAN:
#ifdef SEGMENTS
putsec(secbuf, chinseg);
chinseg = 0;
#endif
vfile("rzfile: zgethdr returned %d", c);
return ERROR;
case ERROR:/* CRC error */
#ifdef SEGMENTS
putsec(secbuf, chinseg);
chinseg = 0;
#endif
if (--n < 0)
{
vfile("rzfile: zgethdr returned %d", c);
return ERROR;
}
zmputs(Attn);
continue;
case TIMEOUT:
#ifdef SEGMENTS
putsec(secbuf, chinseg);
chinseg = 0;
#endif
if (--n < 0)
{
vfile("rzfile: zgethdr returned %d", c);
return ERROR;
}
continue;
case GOTCRCW:
n = 20;
#ifdef SEGMENTS
chinseg += Rxcount;
putsec(secbuf, chinseg);
chinseg = 0;
#else
putsec(secbuf, Rxcount);
#endif
rxbytes += Rxcount;
stohdr(rxbytes);
zshhdr(4, ZACK, Txhdr);
sendline(XON);
goto nxthdr;
case GOTCRCQ:
n = 20;
#ifdef SEGMENTS
chinseg += Rxcount;
#else
putsec(secbuf, Rxcount);
#endif
rxbytes += Rxcount;
stohdr(rxbytes);
zshhdr(4, ZACK, Txhdr);
goto moredata;
case GOTCRCG:
n = 20;
#ifdef SEGMENTS
chinseg += Rxcount;
#else
putsec(secbuf, Rxcount);
#endif
rxbytes += Rxcount;
goto moredata;
case GOTCRCE:
n = 20;
#ifdef SEGMENTS
chinseg += Rxcount;
#else
putsec(secbuf, Rxcount);
#endif
rxbytes += Rxcount;
goto nxthdr;
}
}
}
}
/*
* Send a string to the modem, processing for \336 (sleep 1 sec) and \335
* (break signal)
*/
static void zmputs(char *s)
{
register c;
while (*s)
{
switch (c = *s++)
{
case '\336':
#ifdef __TOWNS__
{
clock_t clk;
clk = H_CLOCK2(0) + 1 * CLOCKS_PER_SEC;
while ( clk > H_CLOCK2(clk) )
;
}
#else
sleep(1);
#endif
continue;
case '\335':
sendbrk();
continue;
default:
sendline(c);
}
}
}
/*
* Close the receive dataset, return OK or ERROR
*/
static int closeit(void)
{
#ifndef __TOWNS__
if (Topipe)
{
if (pclose(fout))
{
return ERROR;
}
return OK;
}
#endif
if (FM_fclose(fout) == ERROR)
{
USR_fprintf(stderr, "file close ERROR\n");
return ERROR;
}
if (Modtime)
{
#ifdef __TOWNS__
struct utimbuf timbuf;
timbuf.actime = time(NULL);
timbuf.modtime = Modtime;
_utime(Pathname, &timbuf);
#else
timep[0] = time(NULL);
timep[1] = Modtime;
utime(Pathname, timep);
#endif
}
if ((Filemode & S_IFMT) == S_IFREG)
#ifdef __TOWNS__
_chmod(Pathname, (07777 & Filemode));
#else
chmod(Pathname, (07777 & Filemode));
#endif
return OK;
}
/*
* Ack a ZFIN packet, let byegones be byegones
*/
static void ackbibi(void)
{
register n;
vfile("ackbibi:");
Readnum = 1;
stohdr(0L);
for (n = 3; --n >= 0;)
{
purgeline();
zshhdr(4, ZFIN, Txhdr);
switch (readline(100))
{
case 'O':
readline(1); /* Discard 2nd 'O' */
vfile("ackbibi complete");
return;
case RCDO:
return;
case TIMEOUT:
default:
break;
}
}
}
/*
* Local console output simulation
*/
static void bttyout(int c)
{
if (Verbose || Fromcu)
USR_fputc(c, stderr);
}
/*
* Strip leading ! if present, do shell escape.
*/
static int sys2(register char *s)
{
if (*s == '!')
++s;
#ifndef __TOWNS__
return system(s);
#else
return (1);
#endif
}
/*
* Strip leading ! if present, do exec.
*/
static void exec2(register char *s)
{
if (*s == '!')
++s;
mode(0);
#ifndef __TOWNS__
execl("/bin/sh", "sh", "-c", s);
#endif
}
/* End of rz.c */